package com.yhy.auth.controller;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.yhy.common.core.annotation.NotControllerResponseAdvice;
import com.yhy.common.core.result.Result;
import com.yhy.common.redis.utils.RedisUtil;
import com.yhy.common.security.constant.SecurityConstants;
import com.yhy.common.security.util.JwtUtil;
import com.yhy.common.security.util.SecurityUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import java.security.KeyPair;
import java.security.Principal;
import java.security.interfaces.RSAPublicKey;
import java.util.Map;
import java.util.concurrent.Executors;

@Api(tags = "认证中心")
@Slf4j
@RestController
@RequestMapping("/oauth")
@RequiredArgsConstructor
public class AuthController {

    final KeyPair keyPair;
    final TokenEndpoint tokenEndpoint;

    @SneakyThrows
    @ApiIgnore
    @ApiOperation(value = "OAuth2认证", notes = "登录入口")
    @PostMapping("/token")
    @NotControllerResponseAdvice
    public Object postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) {
        /*
         * 获取登录认证的客户端ID
         *
         * 兼容两种方式获取Oauth2客户端信息（client_id、client_secret）
         * 方式一：client_id、client_secret放在请求路径中(注：当前版本已废弃)
         * 方式二：放在请求头（Request Headers）中的Authorization字段，且经过加密，例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret
         */
        String clientId = SecurityUtil.getClientId();
        log.info("OAuth认证授权 客户端ID:{}，请求参数：{}", clientId, JSONUtil.toJsonStr(parameters));

        OAuth2AccessToken body = tokenEndpoint.postAccessToken(principal, parameters).getBody();
        /*
         * knife4j接口文档测试使用
         *
         * 请求头自动填充，token必须原生返回，不能有任何包装，否则显示 undefined undefined
         * 账号/密码:  client_id/client_secret : swagger/swagger
         */
        if (body != null && body.getScope().contains(SecurityConstants.SWAGGER_CLIENT)) {
            // swagger 验证不修改返回值，否则swagger 无法认证
            return body;
        }
        return ResponseEntity.ok(Result.ok(body));
    }

    @ApiIgnore
    @ApiOperation(value = "注销")
    @DeleteMapping("/logout")
    public String logout() {
        Executors.newSingleThreadExecutor().submit(() -> {
            JSONObject payload = JwtUtil.getJwtPayload();
            String jti = payload.getStr(SecurityConstants.JWT_JTI);
            RedisUtil.del(SecurityConstants.CACHE_REDIS_ONLINE_USER + jti);
        });
        return "注销成功...";
    }

    @ApiIgnore
    @ApiOperation(value = "获取公钥")
    @GetMapping("/public-key")
    @NotControllerResponseAdvice
    public Map<String, Object> getKey() {
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAKey key = new RSAKey.Builder(publicKey).build();
        Map<String, Object> map = new JWKSet(key).toJSONObject();
        log.info("获取公钥");
        return map;
    }

}
